﻿using CsvHelper;
using CsvHelper.Configuration.Attributes;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;

namespace CableLoadCaseGen
{
    class Program
    {
        static void Main(string[] args)
        {
            //foreach (var ei in Encoding.GetEncodings())
            //{
            //    var e = ei.GetEncoding();

            //    Console.Write("{0,-6} {1,-25} ", ei.CodePage, ei.Name);
            //    Console.Write("{0,-8} {1,-8} ", e.IsBrowserDisplay, e.IsBrowserSave);
            //    Console.Write("{0,-8} {1,-8} ", e.IsMailNewsDisplay, e.IsMailNewsSave);
            //    Console.WriteLine("{0,-8} {1,-8} ", e.IsSingleByte, e.IsReadOnly);
            //}

            var texts = new[]
            {
                "*Node",
                "ST, 一恒, 1",
                "ST, 二恒, 1",
                "*LoadCase, 未知荷载系数, 123, NO, anything"
            };

            File.WriteAllLines("test_gb2312.txt", texts, Encoding.GetEncoding("gb2312"));

            return;

            var workDir = @"F:\坚果云\大桥院\马鞍山大桥\建模\20191202\斜拉索初索力";
            var nodeFileName = "CableNode.txt";
            var elemFileName = "CableElem.txt";
            var constrainDxNodeFileName = "ConstrainNode_DX.txt";
            var constrainDzNodeFileName = "ConstrainNode_DZ.txt";

            var stldcaseFileName = "stldcase.txt";
            var usestldFileName = "usestld.txt";
            var loadCombFileName = "loadComb.txt";
            var unkConsFileName = "unkCons.txt";
            var unkFactorFileName = "unkFactor.txt";

            double pretension = 1D;
            double minDx = -0.04;
            double maxDx = 0.04;
            double minDz = -0.02;
            double maxDz = 0.02;

            // 
            // all nodes
            // 
            Dictionary<int, Node> allNodeDict;
            using (var reader = new StreamReader(Path.Combine(workDir, nodeFileName)))
            using (var csv = new CsvReader(reader))
            {
                csv.Configuration.HasHeaderRecord = false;
                csv.Configuration.Delimiter = "\t";
                allNodeDict = csv.GetRecords<Node>()
                    .ToDictionary(n => n.Id, n => n);
            }

            //
            // all elements
            //
            List<Elem> allElemList;
            using (var reader = new StreamReader(Path.Combine(workDir, elemFileName)))
            using (var csv = new CsvReader(reader))
            {
                csv.Configuration.HasHeaderRecord = false;
                csv.Configuration.Delimiter = "\t";
                allElemList = csv.GetRecords<Elem>()
                    .OrderBy(el => GetUpperLowerNode(el, allNodeDict).LowerNode.X).ToList();
            }

            //
            // constrain nodes
            //
            List<int> ConstrainDxNodes;
            using (var reader = new StreamReader(Path.Combine(workDir, constrainDxNodeFileName)))
            using (var csv = new CsvReader(reader))
            {
                csv.Configuration.HasHeaderRecord = false;
                csv.Configuration.Delimiter = "\t";
                ConstrainDxNodes = csv.GetRecords<Node>()
                    .Select(n => n.Id).ToList();
            }

            List<int> ConstrainDzNodes;
            using (var reader = new StreamReader(Path.Combine(workDir, constrainDzNodeFileName)))
            using (var csv = new CsvReader(reader))
            {
                csv.Configuration.HasHeaderRecord = false;
                csv.Configuration.Delimiter = "\t";
                ConstrainDzNodes = csv.GetRecords<Node>()
                    .Select(n => n.Id).ToList();
            }

            //
            // group cable
            //
            Debug.Assert(allElemList.Count % 4 == 0);
            var elemGroups = new List<List<int>>();
            for (int i = 0; i < allElemList.Count / 4; i++)
            {
                var n1 = allElemList[i * 2].Id;
                var n2 = allElemList[i * 2 + 1].Id;
                var n3 = allElemList[allElemList.Count - 1 - i * 2].Id;
                var n4 = allElemList[allElemList.Count - 1 - i * 2 - 1].Id;
                elemGroups.Add(new List<int> {n1, n2, n3, n4});
            }

            //
            // output
            //
            var outputLines = new List<string>
            {
                "*STLDCASE"
            };
            outputLines.AddRange(Enumerable.Range(0, elemGroups.Count)
                .Select(i => $"T{i + 1}, USER,"));
            File.WriteAllLines(Path.Combine(workDir, stldcaseFileName), outputLines);

            outputLines.Clear();
            outputLines.AddRange(elemGroups.SelectMany((g, i) =>
            {
                var groupLines = new List<string>
                {
                    $"*USE-STLD, T{i + 1}",
                    "*PRETENSION"
                };
                groupLines.AddRange(g.Select(id => $"{id}, {pretension},"));
                return groupLines;
            }));
            File.WriteAllLines(Path.Combine(workDir, usestldFileName), outputLines);

            var loadCombName = "CableUnitForce";
            outputLines.Clear();
            outputLines.AddRange(new[]
            {
                "*LOADCOMB",
                $"NAME={loadCombName}, GEN, ACTIVE, 0, 0, , 0, 0",
                string.Join(", ", Enumerable.Range(0, elemGroups.Count)
                    .Select(i => $"ST, T{i + 1}, 1"))
            });
            File.WriteAllLines(Path.Combine(workDir, loadCombFileName), outputLines);

            // 
            // DX
            // 
            outputLines.Clear();
            outputLines.AddRange(new[]
            {
                "*UNKCONS",
            });
            outputLines.AddRange(ConstrainDxNodes.Select(id =>
                $"DX_N{id}, DISP, {id}, 0, 0, NO, YES, {maxDx}, YES, {minDx}"));
            outputLines.AddRange(ConstrainDzNodes.Select(id =>
                $"DZ_N{id}, DISP, {id}, 0, 2, NO, YES, {maxDz}, YES, {minDz}"));
            File.WriteAllLines(Path.Combine(workDir, unkConsFileName), outputLines);

            outputLines.Clear();
            outputLines.AddRange(new[]
            {
                "*UNKFACTOR",
                "NAME=CableFactor, CableForce, SQUARE, POS, NO"
            });
            outputLines.Add(string.Join(", ",
                ConstrainDxNodes.Select(id => $"DX_N{id}")
                    .Concat(ConstrainDzNodes.Select(id => $"DZ_N{id}"))));
            outputLines.Add(string.Join(", ", Enumerable.Range(0, elemGroups.Count)
                .Select(i => $"T{i + 1}, 1")));
            File.WriteAllLines(Path.Combine(workDir, unkFactorFileName), outputLines);

            //
            Console.WriteLine("Done!");
        }

        private static (Node UpperNode, Node LowerNode) GetUpperLowerNode(Elem el,
            IReadOnlyDictionary<int, Node> nodeDict)
        {
            var n1 = nodeDict[el.N1];
            var n2 = nodeDict[el.N2];
            return n1.Z > n2.Z ? (n1, n2) : (n2, n1);
        }
    }

    public class Node
    {
        [Index(0)] public int Id { get; set; }

        [Index(1)] public double X { get; set; }
        [Index(2)] public double Y { get; set; }
        [Index(3)] public double Z { get; set; }
    }

    public class Elem
    {
        [Index(0)] public int Id { get; set; }
        [Index(6)] public int N1 { get; set; }
        [Index(7)] public int N2 { get; set; }
    }
}